﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace DeepTalkDBSerice
{
    /// <summary>
    /// Rendering interface that a rendering clientn uses to hook up and start rendering things.
    /// </summary>
    [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
    internal class DTRender : IDTRender
    {        
        /// <summary>
        /// The manager that we use to get/set things that are going on!
        /// This is the central repository for the manager. Everyone else should fetch
        /// it from here.
        /// </summary>
        public static ManageRenderClients Manager { get; set; }

        /// <summary>
        /// Internal dude that enables us to do logging.
        /// </summary>
        private Logger _log = new Logger();

        /// <summary>
        /// Get the property setup and working.
        /// </summary>
        static DTRender()
        {
            Manager = new ManageRenderClients();
        }

        /// <summary>
        /// The internal callback class that tracks the guys that can be rendered.
        /// </summary>
        class DTWCFRenderClient : ManageRenderClients.IRenderClient
        {
            IDTRenderCallback _callback = null;

            public bool IsReady { get; set; }

            public DTWCFRenderClient(IDTRenderCallback callback)
            {
                _callback = callback;
            }

            /// <summary>
            /// Do the rendering. But only if the client is ready to go.
            /// </summary>
            /// <param name="id"></param>
            /// <param name="agendaXML"></param>
            /// <returns></returns>
            public bool Render(int id, string agendaXML)
            {
                if (!IsReady)
                {
                    return false;
                }

                IsReady = false;

                DTRenderTalkInfo info = new DTRenderTalkInfo();
                info.ID = id;
                info.AgendaXML = agendaXML;

                IsReady = !_callback.RenderTalk(info);
                return !IsReady;
            }
        }

        /// <summary>
        /// Keep the list of known clients out there.
        /// </summary>
        static Dictionary<string,DTWCFRenderClient> _known_clients = new Dictionary<string,DTWCFRenderClient>();

        /// <summary>
        /// This is called when the client is ready to do some work.
        /// If we've not set this guy up for work, then we need to add it into the
        /// system.
        /// </summary>
        public void ReadyForWork()
        {
            string context = OperationContext.Current.SessionId;
            lock (_known_clients)
            {
                if (!_known_clients.ContainsKey(context))
                {
                    IDTRenderCallback callback = OperationContext.Current.GetCallbackChannel<IDTRenderCallback>();
                    DTWCFRenderClient client = new DTWCFRenderClient(callback);
                    _known_clients[context] = client;
                    Manager.AddClient(client);

                    /// The callbacks to know when the connection is dropped

                    ICommunicationObject comobj = callback as ICommunicationObject;
                    comobj.Closed += (object sender, EventArgs arg) => { Manager.RemoveClient(client); };
                }

                _known_clients[context].IsReady = true;
            }

            ///
            /// Let the manager know that they can re-scan for work
            /// 

            Manager.ReScanForWork();
        }

        #region State Change Operations
        /// <summary>
        /// Action: Render Done. Taks a talk from "unrenereded" to Rendered.
        /// At that point the talk should be in its final directory and all
        /// ready to be served up by a web server.
        /// </summary>
        /// <param name="id"></param>
        /// <param name="megaBytesOnDisk"></param>
        public void RenderDone(int id, float megaBytesOnDisk, int numberOfTalks)
        {
            try
            {
                using (ConferenceModelContainer cont = DBUtilities.GetConferenceContainer())
                {
                    var confs = from c in cont.ConferenceSet
                                where c.Id == id
                                select c;

                    Conference conf = confs.FirstOrDefault();
                    if (conf == null)
                    {
                        throw new ArgumentException("Talk ID=" + id + " does not exist!", "id");
                    }

                    conf.NumberOfTalks = numberOfTalks;

                    cont.SaveChanges();
                }

                ManageTalkState.SetStateRendered(id);
            }
            catch (Exception e)
            {
                _log.LogMessage("Setting a talk to be rendered state", e);
            }
        }

        #endregion

        /// <summary>
        /// Update the talk with new information about title and
        /// start time.
        /// </summary>
        /// <param name="id"></param>
        /// <param name="title"></param>
        /// <param name="startTime"></param>
        public void UpdateConferenceData(int id, string title, DateTime startTime, DateTime endTime)
        {
            using (ConferenceModelContainer cont = DBUtilities.GetConferenceContainer())
            {
                var confs = from c in cont.ConferenceSet
                            where c.Id == id
                            select c;

                Conference conf = confs.FirstOrDefault();
                if (conf == null)
                {
                    throw new ArgumentException("Talk ID=" + id + " does not exist!", "id");
                }
                conf.Title = title;
                conf.StartTime = startTime;
                conf.EndTime = endTime;
                cont.SaveChanges();
            }
        }
    }
}
